In this code, I'll define a function to generate a random portfolio weights, perform an asset allocation, and then and analyze their returns.
First for a equal weighted portfolio and then for random weights, define a function to concatenate portfolios into a DataFrame and finaly plotly data
Install Libs. (remove comments '#' if need to install the libraries)
# !pip install pandas
# !pip install pandas-datareader
# !pip install numpy
# !pip install plotly_express
# !pip install random
#import Libraries
import pandas as pd
from pandas_datareader import data as pdr
import numpy as np
import random
import plotly.graph_objects as go
functions already defined that we use on this code
# Define a function using Plotly Express
def plotly_data(df, title):
# Create figure
fig = go.Figure()
# Set title
fig.update_layout(title_text = title)
# For loop that plots all stock prices in the pandas dataframe df
for i in df.columns[0:]:
# Add range slider
#fig.update_layout(xaxis=dict(rangeselector = dict(buttons=list([dict(count=1, label="1m", step="month", stepmode="backward"), dict(count=6, label="6m", step="month", stepmode="backward"), dict(count=1, label="YTD", step="year", stepmode="todate"), dict(count=1, label="1y", step="year", stepmode="backward"), dict(step="all")])), rangeslider=dict( visible=True), type="date"))
# Add line graph
fig.add_scatter(x = df.index, y = df[i], name = i)
# Update Layout
fig.update_layout({'plot_bgcolor': "white"})
#fig.update_traces(line_width = 3)
fig.update_layout(legend=dict(orientation="h",))
fig.show()
# Define a function using Plotly Express, changes axis y to logarithm scale
def log_plotly_data(df, title):
# Create figure
fig = go.Figure()
# Set title
fig.update_layout(title_text = title)
# For loop that plots all stock prices in the pandas dataframe df
for i in df.columns[0:]:
# Add range slider
#fig.update_layout(xaxis=dict(rangeselector = dict(buttons=list([dict(count=1, label="1m", step="month", stepmode="backward"), dict(count=6, label="6m", step="month", stepmode="backward"), dict(count=1, label="YTD", step="year", stepmode="todate"), dict(count=1, label="1y", step="year", stepmode="backward"), dict(step="all")])), rangeslider=dict( visible=True), type="date"))
# Add line graph
fig.add_scatter(x = df.index, y = df[i], name = i)
# Update Layout
fig.update_layout({'plot_bgcolor': "white"})
#fig.update_traces(line_width = 3)
fig.update_layout(legend=dict(orientation="h",))
#changes y to logarithm scale
fig.update_yaxes(type="log")
fig.show()
# Define a function using Plotly Express, changes axis y to logarithm scale
def plotly_line(df, y, title):
# Create figure
fig = go.Figure()
fig.update_layout(title_text = title)
fig.add_scatter(x = df.index, y = y)
# Update Layout
fig.update_layout({'plot_bgcolor': "white"})
#fig.update_traces(line_width = 3)
fig.update_layout(legend=dict(orientation="h",))
#changes y to logarithm scale
fig.show()
# Define a function using Plotly Express, changes axis y to logarithm scale
def log_plotly_line(df, y, title):
# Create figure
fig = go.Figure()
fig.update_layout(title_text = title)
fig.add_scatter(x = df.index, y = y)
# Update Layout
fig.update_layout({'plot_bgcolor': "white"})
#fig.update_traces(line_width = 3)
fig.update_layout(legend=dict(orientation="h",))
#changes y to logarithm scale
fig.update_yaxes(type="log")
fig.show()
# Function to scale stock prices based on their initial starting price
# The objective of this function is to set all prices to start at a value of 1
def price_scaling(raw_prices_df):
scaled_prices_df = raw_prices_df.copy()
for i in raw_prices_df.columns[0:]:
scaled_prices_df[i] = raw_prices_df[i]/raw_prices_df[i][0]
return scaled_prices_df
file_name = input('Input the CSV file name: ')
initial_investment = int(input('Input the initial investment: '))
n_runs = int(input('Input the number of simulations: '))
Input the CSV file name: BRL10 Input the initial investment: 1000000 Input the number of simulations: 20
#read CSV file
Stock_Prices_df = pd.read_csv(file_name)
#The code imports a DataFrame with num index [1,2,3...], this line replace the colum Date to Index
Stock_Prices_df.set_index(['Date'], inplace = True)
#obtain weights vector
n_assets = len(Stock_Prices_df.columns)
#lock vector to test function
weights = np.ones(n_assets) * 1/n_assets
weights
array([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1])
#Define a function that performs an Asset Allocation
def asset_allocation(df, initial_investment, weights):
''' Performs an asset Allocation for a given DF, initial investment value and weights'''
portfolio_df = df.copy()
# Scale stock prices using the "price_scaling"
scaled_df = price_scaling(df)
#enumerate method links Stocks tickers in columns along with a counter position weight (i), like an index
for i, stock in enumerate(scaled_df):
portfolio_df[stock] = weights[i] * scaled_df[stock] * initial_investment
# Sum up all values and place the result in a new column titled "portfolio value [$]"
portfolio_df['Total Value [$]'] = portfolio_df.sum(axis = 1, numeric_only = True)
# Calculate the portfolio percentage daily return and replace NaNs with zeros
portfolio_df['Daily Return [%]'] = portfolio_df['Total Value [$]'].pct_change(1) * 100
portfolio_df.replace(np.nan, 0, inplace = True)
return portfolio_df
#Asset Allocation with parameters defined
portfolio_df = asset_allocation(Stock_Prices_df, initial_investment, weights)
Eqw = portfolio_df
portfolio_df.round(2)
| CPLE6.SA | CRFB3.SA | ECOR3.SA | ITUB4.SA | JBSS3.SA | PRIO3.SA | RENT3.SA | SBSP3.SA | VALE3.SA | VIVT3.SA | Total Value [$] | Daily Return [%] | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Date | ||||||||||||
| 2018-12-14 | 100000.00 | 100000.00 | 100000.00 | 100000.00 | 100000.00 | 100000.00 | 100000.00 | 100000.00 | 100000.00 | 100000.00 | 1000000.00 | 0.00 |
| 2018-12-17 | 99692.31 | 99555.55 | 97731.96 | 97325.43 | 98509.95 | 104978.13 | 97950.53 | 98694.52 | 100727.19 | 99056.60 | 994222.16 | -0.58 |
| 2018-12-18 | 99384.62 | 100833.31 | 97422.68 | 98904.25 | 98675.50 | 98269.33 | 97597.19 | 98074.41 | 101100.60 | 100856.98 | 991118.87 | -0.31 |
| 2018-12-19 | 96553.83 | 97333.32 | 96907.21 | 97580.61 | 95529.80 | 99183.28 | 98339.21 | 97225.86 | 98290.08 | 98575.67 | 975518.87 | -1.57 |
| 2018-12-20 | 96461.52 | 100333.34 | 97113.40 | 97552.45 | 94370.86 | 96246.96 | 99346.72 | 99869.46 | 97897.01 | 97278.95 | 976470.66 | 0.10 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2023-12-07 | 469772.65 | 69389.34 | 95695.89 | 108459.32 | 247430.61 | 2062226.53 | 244892.92 | 248979.82 | 209180.82 | 181340.74 | 3937368.64 | 1.12 |
| 2023-12-08 | 470264.01 | 67795.60 | 95904.60 | 109831.35 | 250366.22 | 2165775.45 | 243260.03 | 247953.70 | 209757.09 | 182260.56 | 4043168.61 | 2.69 |
| 2023-12-11 | 468298.44 | 67979.49 | 97574.33 | 109076.73 | 249108.11 | 2172095.38 | 243459.16 | 252681.14 | 209929.96 | 180250.58 | 4050453.32 | 0.18 |
| 2023-12-12 | 469772.65 | 69450.64 | 96426.39 | 108527.91 | 247430.61 | 2129314.58 | 241029.74 | 253707.28 | 210304.52 | 181783.62 | 4007747.96 | -1.05 |
| 2023-12-13 | 474195.16 | 70002.33 | 98096.11 | 110140.06 | 249632.31 | 2163830.82 | 245888.58 | 260377.04 | 209238.46 | 182839.70 | 4064240.57 | 1.41 |
1240 rows × 12 columns
#Plot data:
plotly_line(portfolio_df, portfolio_df['Total Value [$]'], "Portfolio Total Value")
log_plotly_line(portfolio_df, portfolio_df['Total Value [$]'], "Portfolio Total Value - Log Scale")
plotly_line(portfolio_df, portfolio_df['Daily Return [%]'], "Daily Return [%]")
plotly_data(portfolio_df, "Equal Weighted Porfolio")
log_plotly_data(portfolio_df, "Equal Weighted Porfolio")
def rand_weights(n):
''' Produces n random weights that sum to 1 '''
k = np.random.rand(n)
return k / sum(k)
#obtain weights vector
n_assets = len(Stock_Prices_df.columns)
weights = rand_weights(n_assets).round(4)
display(weights)
sum(weights)
array([0.1326, 0.001 , 0.1646, 0.0679, 0.1769, 0.0038, 0.1017, 0.0664,
0.1274, 0.1576])
0.9999
#Eqw -- Equal Weighted </p>
#Rdw_1 -- Random Weighted 1 </p>
#Rdw_2 -- Random Weighted 2 [...]
def random_port_generate(initial_investment, n_runs):
#obtain weights vector
n_assets = len(Stock_Prices_df.columns)
#lock vector to test function
eq_weights = np.ones(n_assets) * 1/n_assets
#Asset Allocation with parameters defined
Eqw_df = asset_allocation(Stock_Prices_df, initial_investment, eq_weights)[['Total Value [$]', 'Daily Return [%]']]
Eqw_df = Eqw_df.rename({'Total Value [$]':'Eqw [$]', 'Daily Return [%]':'Eqw [%]'}, axis="columns")
All_df = Eqw_df
# Placeholder to store all weights
weights_runs = np.zeros((n_runs, n_assets))
for i in range(n_runs):
# Generate random weights
weights = rand_weights(n_assets)
# Store the weights
weights_runs[i,:] = weights
# Random Asset Allocation
df = asset_allocation(Stock_Prices_df, initial_investment, weights)[['Total Value [$]', 'Daily Return [%]']]
#rename columns for iterate
Rdw = df.rename({'Total Value [$]':'Rdw_{}[$]'.format(i),
'Daily Return [%]':'Rdw_{}[%]'.format(i)}, axis="columns")
All_df = pd.merge(All_df, Rdw, on = 'Date')
#All_df = Eqw_df.join(Rdw)
print("Simulation Run = {}".format(i))
print("Weights = {}".format(weights_runs[i].round(3)))
print('\n')
return All_df
df = random_port_generate(initial_investment, n_runs)
daily_returns_df = df.iloc[:, 1::2]
total_values_df = df.iloc[:, 0::2]
display(df.round(2))
Simulation Run = 0 Weights = [0.078 0.081 0.114 0.144 0.188 0.017 0.072 0.102 0.023 0.18 ] Simulation Run = 1 Weights = [0.011 0.053 0.187 0.161 0.201 0.087 0.089 0.012 0.095 0.105] Simulation Run = 2 Weights = [0.144 0.11 0.045 0.023 0.142 0.131 0.079 0.158 0.16 0.008] Simulation Run = 3 Weights = [0.146 0.062 0.201 0.012 0.177 0.007 0.058 0.158 0.151 0.028] Simulation Run = 4 Weights = [0.161 0.104 0.021 0.162 0.173 0.013 0.11 0.149 0.064 0.042] Simulation Run = 5 Weights = [0.157 0.152 0.05 0.146 0.037 0.078 0.121 0.094 0.023 0.143] Simulation Run = 6 Weights = [0.03 0.213 0.168 0.082 0.13 0.058 0.102 0.014 0.135 0.069] Simulation Run = 7 Weights = [0.103 0.004 0.109 0.088 0.127 0.177 0.027 0.022 0.203 0.141] Simulation Run = 8 Weights = [0.061 0.187 0.088 0.216 0.016 0.048 0.059 0.048 0.111 0.167] Simulation Run = 9 Weights = [0.087 0.12 0.045 0.19 0.007 0.14 0.155 0.077 0.169 0.009] Simulation Run = 10 Weights = [0.039 0.142 0.048 0.103 0.045 0.095 0.136 0.155 0.014 0.224] Simulation Run = 11 Weights = [0.145 0.08 0.133 0.063 0.103 0.032 0.077 0.092 0.153 0.122] Simulation Run = 12 Weights = [0.093 0.019 0.172 0.115 0.183 0.048 0.078 0.159 0.018 0.116] Simulation Run = 13 Weights = [0.184 0.165 0. 0.079 0.086 0.09 0.175 0.056 0.107 0.057] Simulation Run = 14 Weights = [0.088 0.148 0.055 0.035 0.126 0.185 0.103 0.066 0.161 0.031] Simulation Run = 15 Weights = [0.158 0.196 0.037 0.143 0.104 0.054 0.01 0.053 0.048 0.197] Simulation Run = 16 Weights = [0.288 0.103 0.045 0.01 0.284 0.056 0.116 0.03 0.007 0.061] Simulation Run = 17 Weights = [0.183 0.145 0.157 0.091 0.129 0.066 0.032 0.149 0.043 0.007] Simulation Run = 18 Weights = [0.111 0.022 0.083 0.267 0.142 0.009 0.04 0.198 0.093 0.035] Simulation Run = 19 Weights = [0.104 0.197 0.109 0.038 0.026 0.119 0.068 0.172 0.057 0.108]
| Eqw [$] | Eqw [%] | Rdw_0[$] | Rdw_0[%] | Rdw_1[$] | Rdw_1[%] | Rdw_2[$] | Rdw_2[%] | Rdw_3[$] | Rdw_3[%] | ... | Rdw_15[$] | Rdw_15[%] | Rdw_16[$] | Rdw_16[%] | Rdw_17[$] | Rdw_17[%] | Rdw_18[$] | Rdw_18[%] | Rdw_19[$] | Rdw_19[%] | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Date | |||||||||||||||||||||
| 2018-12-14 | 1000000.00 | 0.00 | 1000000.00 | 0.00 | 1000000.00 | 0.00 | 1000000.00 | 0.00 | 1000000.00 | 0.00 | ... | 1000000.00 | 0.00 | 1000000.00 | 0.00 | 1000000.00 | 0.00 | 1000000.00 | 0.00 | 1000000.00 | 0.00 |
| 2018-12-17 | 994222.16 | -0.58 | 986669.06 | -1.33 | 990235.99 | -0.98 | 999244.02 | -0.08 | 989696.84 | -1.03 | ... | 992686.81 | -0.73 | 992639.11 | -0.74 | 991807.83 | -0.82 | 985813.02 | -1.42 | 996598.92 | -0.34 |
| 2018-12-18 | 991118.87 | -0.31 | 990970.19 | 0.44 | 989207.48 | -0.10 | 991363.92 | -0.79 | 989325.99 | -0.04 | ... | 996792.18 | 0.41 | 990347.36 | -0.23 | 989111.27 | -0.27 | 988962.66 | 0.32 | 991956.89 | -0.47 |
| 2018-12-19 | 975518.87 | -1.57 | 972598.37 | -1.85 | 973909.57 | -1.55 | 974206.35 | -1.73 | 970500.65 | -1.90 | ... | 974357.48 | -2.25 | 968799.76 | -2.18 | 971018.43 | -1.83 | 971894.81 | -1.73 | 976275.72 | -1.58 |
| 2018-12-20 | 976470.66 | 0.10 | 973491.12 | 0.09 | 970427.20 | -0.36 | 976201.94 | 0.20 | 974186.47 | 0.38 | ... | 976106.22 | 0.18 | 967921.79 | -0.09 | 976074.34 | 0.52 | 975469.72 | 0.37 | 982114.60 | 0.60 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2023-12-07 | 3937368.64 | 1.12 | 2315441.90 | 0.16 | 3363535.73 | 1.13 | 4809275.02 | 1.24 | 2415393.28 | -0.32 | ... | 3046273.08 | 0.83 | 3816767.24 | 0.80 | 3428285.62 | 0.65 | 2288196.02 | -0.03 | 4207638.81 | 1.13 |
| 2023-12-08 | 4043168.61 | 2.69 | 2339727.46 | 1.05 | 3460941.36 | 2.90 | 4946458.68 | 2.85 | 2426488.55 | 0.46 | ... | 3106006.72 | 1.96 | 3881391.59 | 1.69 | 3498381.90 | 2.04 | 2303808.16 | 0.68 | 4328503.66 | 2.87 |
| 2023-12-11 | 4050453.32 | 0.18 | 2339308.69 | -0.02 | 3464478.60 | 0.10 | 4958629.03 | 0.25 | 2432470.56 | 0.25 | ... | 3103531.01 | -0.08 | 3876988.37 | -0.11 | 3506544.68 | 0.23 | 2308727.79 | 0.21 | 4341764.59 | 0.31 |
| 2023-12-12 | 4007747.96 | -1.05 | 2331136.56 | -0.35 | 3421863.64 | -1.23 | 4903761.07 | -1.11 | 2428515.11 | -0.16 | ... | 3086325.63 | -0.55 | 3851971.76 | -0.65 | 3479876.07 | -0.76 | 2304022.85 | -0.20 | 4295236.48 | -1.07 |
| 2023-12-13 | 4064240.57 | 1.41 | 2361363.43 | 1.30 | 3467915.95 | 1.35 | 4972940.79 | 1.41 | 2457119.44 | 1.18 | ... | 3123709.81 | 1.21 | 3899899.70 | 1.24 | 3529376.42 | 1.42 | 2335442.76 | 1.36 | 4360441.73 | 1.52 |
1240 rows × 42 columns
plotly_data(total_values_df, "Portfolios Total Value[$]")
log_plotly_data(total_values_df, "Portfolios Total Value[$]")
plotly_data(daily_returns_df, "Portfolio Daily Returns [%]")
import plotly.express as px
# Plot histograms for stocks daily returns using plotly express
fig = px.histogram(daily_returns_df)
fig.update_layout({'plot_bgcolor': "white"})